home *** CD-ROM | disk | FTP | other *** search
- ; Filename: cpuid32.msm
- ;
- ; This program has been developed by Intel Corporation. You have
- ; Intel's permission to incorporate this source code into your
- ; product royalty free.
- ;
- ; Intel specifically disclaims all warranties, express or implied,
- ; and all liability, including consequential and other indirect
- ; damages, for the use of this code, including liability for
- ; infringement of any proprietary rights. Intel does not assume
- ; any responsibility for any errors which may appear in this code
- ; nor any responsibility to update it.
- ;
- ; This program contains three parts:
- ; Part 1: Identifies CPU type in the variable cpu_type:
- ; 0=8086 processor
- ; 2=Intel 286 processor
- ; 3=Intel386(TM) processor
- ; 4=Intel486(TM) processor
- ; 5=Pentium(TM) processor
- ;
- ; Part 2: Identifies FPU type in the variable fpu_type:
- ; 0=FPU not present
- ; 1=FPU present
- ; 2=287 present (only if cpu_type=3)
- ; 3=387 present (only if cpu_type=3)
- ;
- ; Part 3: Prints out the appropriate message. This part can
- ; be removed if this program is not used in a DOS-based
- ; system. Portions affected are at the end of the
- ; data segment and the print procedure in the code
- ; segment.
- ;
- ; REVISION HISTORY:
- ; Date: 4/93
- ; 1. Replaced the .486 with .186 to avoid generation of 0FH type long
- ; conditional branches (such as the branch to end_get_cpuid at
- ; the end of the 8086/8088 test, near code address 003D). These
- ; are not executable on the 8086/8088 and 80286, so the code falls
- ; into some strange place and hangs the system. The .186 also allows
- ; multi-bit shifts to unpack CPUID info. Using .186 requires
- ; that all 32-bit operand prefix (66H) be inserted by hand.
- ; This is done with a macro (OPND32).
- ; 2. Avoid all usage of 32-bit operands until it is clear that the
- ; CPU is at least an 80386. The use of the Exx registers caused
- ; the generation of the 66H prefix, which are not executed
- ; correctly on the 8086/8088 and 80286.
- ; 3. Eliminated all the register save/restore and added comment
- ; that all registers are used by the functions.
- ; 4. Do the stack alignment just before messing with the AC bit in
- ; EFLAGS, otherwise the stack may not be properly aligned. Also
- ; restore the AC bit immediately, so it does not stay set.
- ; 5. Changed the FPU detection to set one flag fpu_type (instead of
- ; the previous two flags: fpu_present and infinity). An fpu_type
- ; of zero indicates no floating point unit is present, an fpu_type
- ; of 2 indicates an 80287 is present, an fpu_type of 3 indicates
- ; an 80387 is present.
- ;
- ; If this code is assembled with MASM with no options specified, it
- ; runs correctly on an 8086/8088, 80286, 80386, 80486, and
- ; Pentium(tm) processor.
- ;
-
-
- TITLE CPUID
- DOSSEG
- .model small
- .stack 100h
- .186
-
- OPND32 MACRO op_code, op_erand
- db 66h ; Force 32-bit operand size
- IFNB <op_code>
- db op_code
- IFNB <op_erand>
- dd op_erand; 32-bit immediate value
- ENDIF
- ENDIF
- ENDM
-
- CPUID MACRO
- db 0fh ; Hardcoded opcode for CPUID instruction
- db 0a2h
- ENDM
-
- TRUE equ 1
- FAMILY_MASK equ 0f00h
- FAMILY_SHIFT equ 8
- MODEL_MASK equ 0f0h
- MODEL_SHIFT equ 4
- STEPPING_MASK equ 0fh
- FPU_FLAG equ 1h
- MCE_FLAG equ 80h
- CMPXCHG8B_FLAG equ 100h
-
- .data
- fp_status dw ?
- vendor_id db 12 dup (?)
- cpu_type db ?
- modell db ?
- stepping db ?
- id_flag db 0
- fpu_type db 0
- intel_proc db 0
- feature_flags dw 2 dup (0)
- ;
- ; remove the remaining data declarations if not using the DOS-based
- ; print procedure
- ;
- id_msg db "This system has a$"
- fp_8087 db " and an 8087 math coprocessor$"
- fp_80287 db " and an 80287 math coprocessor$"
- fp_80387 db " and an 80387 math coprocessor$"
- c8086 db "n 8086/8088 processor$"
- c286 db "n 80286 processor$"
- c386 db "n 80386 processor$"
- c486 db "n 80486 DX processor or 80487 SX math coprocessor$"
- c486nfp db "n 80486 SX processor$"
- Intel486_msg db 13,10,"This system contains a Genuine Intel486(TM) processor",13,10,"$"
- Pentium_msg db 13,10,"This system contains a Genuine Intel Pentium(TM) processor",13,10,"$"
- modelmsg db "Model: $"
- steppingmsg db "Stepping: $"
- familymsg db 13,10,"Processor Family: $"
- period db ".",13,10,"$"
- dataCR db ?,13,10,"$"
- intel_id db "GenuineIntel"
- fpu_msg db 13,10,"This processor contains a FPU",13,10,"$"
- mce_msg db "This processor supports the Machine Check Exception",13,10,"$"
- cmp_msg db "This processor supports the CMPXCHG8B instruction",13,10,"$"
- not_intel db "t least an 80486 processor.",13,10
- db "It does not contain a Genuine Intel part and as a result,",13,10
- db "the CPUID detection information cannot be determined at this time.",13,10,"$"
-
- ;
- ; The purpose of this code is to identify the processor and
- ; coprocessor that is currently in the system. The program first
- ; determines the processor id. When that is accomplished,
- ; the program then determines whether a coprocessor
- ; exists in the system. If a coprocessor or integrated
- ; coprocessor exists, the program identifies
- ; the coprocessor id. The program then prints out
- ; the CPU and floating point presence and type.
- ;
- .code
- start: mov ax, @data
- mov ds, ax ; set segment register
- mov es, ax ; set segment register
- pushf ; save for restoration at end
- call get_cpuid
- call get_fpuid
- call print
- popf
- mov ax, 4c00h ; terminate program
- int 21h
-
-
- get_cpuid proc
- ;
- ; This procedure determines the type of CPU in a system
- ; and sets the cpu_type variable with the appropriate
- ; value.
- ; All registers are used by this procedure, none are preserved.
-
- ; Intel 8086 CPU check
- ; Bits 12-15 of the FLAGS register are always set on the
- ; 8086 processor.
- ;
- check_8086:
- pushf ; push original FLAGS
- pop ax ; get original FLAGS
- mov cx, ax ; save original FLAGS
- and ax, 0fffh ; clear bits 12-15 in FLAGS
- push ax ; save new FLAGS value on stack
- popf ; replace current FLAGS value
- pushf ; get new FLAGS
- pop ax ; store new FLAGS in AX
- and ax, 0f000h ; if bits 12-15 are set, then CPU
- cmp ax, 0f000h ; is an 8086/8088
- mov cpu_type, 0 ; turn on 8086/8088 flag
- jne check_80286 ; jump if CPU is not 8086/8088
- jmp end_get_cpuid
- ; Intel 286 CPU check
- ; Bits 12-15 of the FLAGS register are always clear on the
- ; Intel 286 processor in real-address mode.
- ;
- check_80286:
- or cx, 0f000h ; try to set bits 12-15
- push cx ; save new FLAGS value on stack
- popf ; replace current FLAGS value
- pushf ; get new FLAGS
- pop ax ; store new FLAGS in AX
- and ax, 0f000h ; if bits 12-15 clear, CPU=80286
- mov cpu_type, 2 ; turn on 80286 flag
- jnz check_80386 ; if no bits set, CPU is 80286
- jmp end_get_cpuid
- ; Intel386 CPU check
- ; The AC bit, bit #18, is a new bit introduced in the EFLAGS
- ; register on the Intel486 DX CPU to generate alignment faults.
- ; This bit cannot be set on the Intel386 CPU.
- ;
- check_80386:
- ; It is now safe to use 32-bit opcode/operands
- mov bx, sp ; save current stack pointer to align
- and sp, not 3 ; align stack to avoid AC fault
- OPND32
- pushf ; push original EFLAGS
- OPND32
- pop ax ; get original EFLAGS
- OPND32
- mov cx, ax ; save original EFLAGS
- OPND32 35h, 40000h ; flip AC bit in EFLAGS
- OPND32
- push ax ; save new EFLAGS value on stack
- OPND32
- popf ; replace current EFLAGS value
- OPND32
- pushf ; get new EFLAGS
- OPND32
- pop ax ; store new EFLAGS in EAX
- OPND32
- xor ax, cx ; can't toggle AC bit, CPU=80386
- mov cpu_type, 3 ; turn on 80386 CPU flag
- mov sp, bx ; restore original stack pointer
- jz end_get_cpuid ; jump if 80386 CPU
- and sp, not 3 ; align stack to avoid AC fault
- OPND32
- push cx
- OPND32
- popf ; restore AC bit in EFLAGS first
- mov sp, bx ; restore original stack pointer
-
- ; Intel486 DX CPU, Intel487 SX NDP, and Intel486 SX CPU check
- ; Checking for ability to set/clear ID flag (Bit 21) in EFLAGS
- ; which indicates the presence of a processor
- ; with the ability to use the CPUID instruction.
- ;
- check_80486:
- mov cpu_type, 4 ; turn on 80486 CPU flag
- OPND32
- mov ax, cx ; get original EFLAGS
- OPND32 35h, 200000h ; flip ID bit in EFLAGS
- OPND32
- push ax ; save new EFLAGS value on stack
- OPND32
- popf ; replace current EFLAGS value
- OPND32
- pushf ; get new EFLAGS
- OPND32
- pop ax ; store new EFLAGS in EAX
- OPND32
- xor ax, cx ; can't toggle ID bit,
- je end_get_cpuid ; CPU=80486
-
- ; Execute CPUID instruction to determine vendor, family,
- ; model and stepping.
- ;
- check_vendor:
- mov id_flag, 1 ; set flag indicating use of CPUID inst.
- OPND32
- xor ax, ax ; set up input for CPUID instruction
- CPUID ; macro for CPUID instruction
- OPND32
- mov word ptr vendor_id, bx ; setup to test for vendor id
- OPND32
- mov word ptr vendor_id[+4], dx
- OPND32
- mov word ptr vendor_id[+8], cx
- mov si, offset vendor_id
- mov di, offset intel_id
- mov cx, length intel_id
- compare:
- repe cmpsb ; compare vendor id to "GenuineIntel"
- or cx, cx
- jnz end_get_cpuid ; if not zero, not an Intel CPU,
-
- intel_processor:
- mov intel_proc, 1
-
- cpuid_data:
- OPND32
- cmp ax, 1 ; make sure 1 is a valid input
- ; value for CPUID
- jl end_get_cpuid ; if not, jump to end
- OPND32
- xor ax, ax ; otherwise, use as input to CPUID
- OPND32
- inc ax ; and get stepping, model and family
- CPUID
- mov stepping, al
- and stepping, STEPPING_MASK ; isolate stepping info
-
- and al, MODEL_MASK ; isolate model info
- shr al, MODEL_SHIFT
- mov modell, al
-
- and ax, FAMILY_MASK ; mask everything but family
- shr ax, FAMILY_SHIFT
- mov cpu_type, al ; set cpu_type with family
-
- OPND32
- mov feature_flags, dx ; save feature flag data
-
- end_get_cpuid:
- ret
- get_cpuid endp
-
-
- ;******************************************************************
-
- get_fpuid proc
- ;
- ; This procedure determines the type of FPU in a system
- ; and sets the fpu_type variable with the appropriate
- ; value.
- ; All registers are used by this procedure, none are preserved.
-
- ; Coprocessor check
- ; The algorithm is to determine whether the floating-point
- ; status and control words can be written to. If not, no
- ; coprocessor exists. If the status and control words can be
- ; written to, the correct coprocessor is then determined
- ; depending on the processor id. The Intel386 CPU can
- ; work with either an Intel287 NDP or an Intel387 NDP.
- ; The infinity of the coprocessor must be
- ; checked to determine the correct coprocessor id.
-
- fninit ; reset FP status word
- mov fp_status, 5a5ah; initialize temp word to
- ; non-zero value
- fnstsw fp_status ; save FP status word
- mov ax, fp_status ; check FP status word
- cmp al, 0 ; see if correct status with
- ; written
- mov fpu_type, 0 ; no fpu present
- jne end_get_fpuid
-
- check_control_word:
- fnstcw fp_status ; save FP control word
- mov ax, fp_status ; check FP control word
- and ax, 103fh ; see if selected parts
- ; looks OK
- cmp ax, 3fh ; check that 1's & 0's
- ; correctly read
- mov fpu_type, 0
- jne end_get_fpuid
- mov fpu_type, 1
-
- ;
- ; 80287/80387 check for the Intel386 CPU
- ;
- check_infinity:
- cmp cpu_type, 3
- jne end_get_fpuid
- fld1 ; must use default control from FNINIT
- fldz ; form infinity
- fdiv ; 8087 and Intel287 NDP say +inf = -inf
- fld st ; form negative infinity
- fchs ; Intel387 NDP says +inf <> -inf
- fcompp ; see if they are the same and remove them
- fstsw fp_status ; look at status from FCOMPP
- mov ax, fp_status
- mov fpu_type, 2 ; store Intel287 NDP for fpu type
- sahf ; see if infinities matched
- jz end_get_fpuid ; jump if 8087 or Intel287 is present
- mov fpu_type, 3 ; store Intel387 NDP for fpu type
- end_get_fpuid:
- ret
- get_fpuid endp
-
-
- ;*********************************************************************
-
- print proc
- ;
- ; This procedure prints the appropriate cpuid string and
- ; numeric processor presence status. If the CPUID instruction
- ; was supported, this procedure prints out cpuid info.
- ; All registers are used by this procedure, none are preserved.
-
- cmp id_flag, 1 ; if set to 1, cpu supports
- ; CPUID instruction
- ; print detailed CPUID information
- jne cont
- jmp print_cpuid_data
-
- cont: mov dx, offset id_msg ; print initial message
- mov ah, 9h
- int 21h
-
- print_86:
- cmp cpu_type, 0
- jne print_286
- mov dx, offset c8086
- mov ah, 9h
- int 21h
- cmp fpu_type, 0
- jne cont2
- jmp end_print
- cont2: mov dx, offset fp_8087
- mov ah, 9h
- int 21h
- jmp end_print
-
- print_286:
- cmp cpu_type, 2
- jne print_386
- mov dx, offset c286
- mov ah, 9h
- int 21h
- cmp fpu_type, 0
- jne cont3
- jmp end_print
- cont3: mov dx, offset fp_80287
- mov ah, 9h
- int 21h
- jmp end_print
-
- print_386:
- cmp cpu_type, 3
- jne print_486
- mov dx, offset c386
- mov ah, 9h
- int 21h
- cmp fpu_type, 0
- jne cont4
- jmp end_print
- cont4: cmp fpu_type, 2
- jne print_387
- mov dx, offset fp_80287
- mov ah, 9h
- int 21h
- jmp end_print
-
- print_387:
- mov dx, offset fp_80387
- mov ah, 9h
- int 21h
- jmp end_print
-
- print_486:
- cmp fpu_type, 0
- je print_Intel486sx
- mov dx, offset c486
- mov ah, 9h
- int 21h
- jmp end_print
-
- print_Intel486sx:
- mov dx, offset c486nfp
- mov ah, 9h
- int 21h
- jmp end_print
-
- print_cpuid_data:
-
- cmp_vendor:
- cmp intel_proc, 1
- je cont5
- jmp not_GenuineIntel
- cont5: cmp cpu_type, 4 ; if cpu_type=4, print
- ; Intel486 CPU message
- jne check_Pentium
- mov dx, offset Intel486_msg
- mov ah, 9h
- int 21h
- jmp print_family
-
- check_Pentium:
- cmp cpu_type, 5 ; if cpu_type=5, print
- jne print_features ; Pentium processor message
- mov dx, offset Pentium_msg
- mov ah, 9h
- int 21h
-
- print_family:
- mov dx, offset familymsg ; print family msg
- mov ah, 9h
- int 21h
- mov al, cpu_type
- mov byte ptr dataCR, al
- add byte ptr dataCR, 30h ; convert to ASCII
- mov dx, offset dataCR ; print family info
- mov ah, 9h
- int 21h
-
- print_model:
- mov dx, offset modelmsg ; print model msg
- mov ah, 9h
- int 21h
- mov al, modell
- mov byte ptr dataCR, al
- add byte ptr dataCR, 30h ; convert to ASCII
- mov dx, offset dataCR ; print model info
- mov ah, 9h
- int 21h
-
- print_stepping:
- mov dx, offset steppingmsg ; print stepping msg
- mov ah, 9h
- int 21h
- mov al, stepping
- mov byte ptr dataCR, al
- add byte ptr dataCR, 30h ; convert to ASCII
- mov dx, offset dataCR ; print stepping info
- mov ah, 9h
- int 21h
-
- print_features:
- mov ax, feature_flags
- and ax, FPU_FLAG ; check for FPU
- jz check_MCE
- mov dx, offset fpu_msg
- mov ah, 9h
- int 21h
-
- check_MCE:
- mov ax, feature_flags
- and ax, MCE_FLAG ; check for MCE
- jz check_CMPXCHG8B
- mov dx, offset mce_msg
- mov ah, 9h
- int 21h
-
- check_CMPXCHG8B:
- mov ax, feature_flags
- and ax, CMPXCHG8B_FLAG ; check for CMPXCHG8B
- jz end_print
- mov dx, offset cmp_msg
- mov ah, 9h
- int 21h
- jmp end_print
-
- not_GenuineIntel:
- mov dx, offset not_Intel
- mov ah, 9h
- int 21h
-
- end_print:
- ret
- print endp
-
- end start
-